package top.sdwu_top.kbase.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import top.sdwu_top.kbase.common.aspect.exception.MyException;
import top.sdwu_top.kbase.common.constants.Constants;
import top.sdwu_top.kbase.common.enums.ApplyStateEnums;
import top.sdwu_top.kbase.common.enums.VisibleTypeEnum;
import top.sdwu_top.kbase.dto.KbaseFileDto;
import top.sdwu_top.kbase.entity.*;
import top.sdwu_top.kbase.entity.elastic.EsFile;
import top.sdwu_top.kbase.mapper.KbaseFileMapper;
import top.sdwu_top.kbase.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import top.sdwu_top.kbase.util.file.FileUtil;
import top.sdwu_top.kbase.vo.KbaseFileVo;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Observable;
import java.util.stream.Collectors;

/**
 * <p>
 * 文件表 服务实现类
 * </p>
 *
 * @author zhipeng.duan@nvr-china.com
 * @since 2020-02-20
 */
@Slf4j
@Service
public class KbaseFileServiceImpl extends ServiceImpl<KbaseFileMapper, KbaseFile> implements IKbaseFileService {

    @Autowired
    FileUtil fileUtil;

    @Autowired
    IKbaseFileVersionService versionService;

    @Autowired
    IKbaseAuthOrgService orgService;

    @Autowired
    IKbaseFileShareService shareService;

    @Autowired
    IKbaseKnowledgeTagService tagService;

    @Autowired
    IKbaseFileTagService fileTagService;

    @Autowired
    IKbaseFileVersionApplyService applyService;

    @Autowired
    IEsFileService esFileService;




    @Override
    public KbaseFile upload(KbaseAuthUser user, MultipartFile file, String fileName, String visibleType, String tagList) {
        if (Objects.isNull(file) || StringUtils.isAnyBlank(fileName, visibleType)) {
            throw new MyException(403, "字段校验失败");
        }
        //在文件表新增
        FileData fileData = fileUtil.saveFile(file);
        KbaseFile kbaseFile = new KbaseFile();
        kbaseFile.setFileName(fileName);
        kbaseFile.setFilePath(fileData.getFilePath());
        kbaseFile.setFileType(fileData.getFileType());
        kbaseFile.setStoreType(fileUtil.getStoreType());
        kbaseFile.setOrgId(user.getOrgId());
        kbaseFile.setVisibleType(visibleType);

        //设置用户信息
        kbaseFile.setOrgId(user.getOrgId());
        boolean flag = super.save(kbaseFile);
        if (flag) {
            //在文件版本表新增
            KbaseFileVersion fileVersion = new KbaseFileVersion();
            fileVersion.setFileId(kbaseFile.getId());
            fileVersion.setFileType(kbaseFile.getFileType());
            fileVersion.setStoreType(kbaseFile.getStoreType());
            fileVersion.setFileVersionPath(kbaseFile.getFilePath());
            fileVersion.setVersionBy(user.getId());
            fileVersion.setVersion(1);
            fileVersion.setContent("初次上传");
            versionService.save(fileVersion);
            //添加文件tag
            if (StringUtils.isNotBlank(tagList)) {
                String[] tags = tagList.split(",");
                for (String tag : tags) {
                    KbaseFileTag fileTag = new KbaseFileTag();
                    fileTag.setFileId(kbaseFile.getId());
                    fileTag.setTagId(tag);
                    fileTagService.save(fileTag);
                }
            }
            //最后追加入检索数据库
            if (StringUtils.equalsIgnoreCase("markdown", fileData.getFileType())) {
                String content = readTextFromFile(fileData.getFilePath());
                EsFile esFile = new EsFile();
                esFile.setContent(content);
                esFile.setId(kbaseFile.getId());
                esFile.setFileName(kbaseFile.getFileName());
                if (StringUtils.isNotBlank(tagList)) {
                    String[] tags = tagList.split(",");
                    List<String> tagNameList = tagService.getNameListByIdList(Arrays.asList(tags));
                    esFile.setTags(StringUtils.join(tagNameList, ","));
                }
                esFileService.save(esFile);
            }

        }

        return kbaseFile;
    }

    @Override
    @Transactional
    public KbaseFileVersionApply updateFile(KbaseAuthUser user, String fileId, MultipartFile file, String content) {
        if (Objects.isNull(file) || StringUtils.isAnyBlank(fileId)) {
            throw new MyException(403, "字段校验失败");
        }
        KbaseFile kbaseFile = super.getById(fileId);
        if (Objects.isNull(kbaseFile)) {
            throw new MyException(50003, "没有此文件");
        }
        FileData fileData = fileUtil.saveFile(file);

        //新增申请
        KbaseFileVersionApply apply = new KbaseFileVersionApply();
        apply.setFileId(fileId);
        apply.setFileType(fileData.getFileType());
        apply.setStoreType(fileUtil.getStoreType());
        if (StringUtils.isNotBlank(content) && !"null".equals(content.toLowerCase())) {
            apply.setContent(content);
        } else {
            apply.setContent("暂无");
        }
        apply.setFileVersionPath(fileData.getFilePath());
        apply.setOwner(kbaseFile.getCreatedBy());
        apply.setState(ApplyStateEnums.APPLYING.getKey());
        boolean flag = applyService.save(apply);
        if (flag) {
            if (isCreator(user, fileId)) {
                applyService.pass(user, apply.getId());
            }
        }
        return apply;
    }


    @Override
    public void download(HttpServletRequest request, HttpServletResponse response, String id, Integer version, String applyId) {
        KbaseFile file = super.getById(id);
        if (Objects.isNull(file)) {
            return;
        }
        //如果有版本号
        if (Objects.nonNull(version)) {
            KbaseFileVersion fileVersion = versionService.getByFileIdAndVersion(id, version);
            if (Objects.isNull(fileVersion)) {
                return;
            }
            file.setFilePath(fileVersion.getFileVersionPath());
            file.setFileType(fileVersion.getFileType());
        }
        //如果有申请号
        if (StringUtils.isNotBlank(applyId)) {
            KbaseFileVersionApply apply = applyService.getById(applyId);
            if (Objects.isNull(apply)) {
                return;
            }
            file.setFilePath(apply.getFileVersionPath());
            file.setFileType(apply.getFileType());
        }
        //1.设置文件ContentType类型，这样设置，会自动判断下载文件类型
        response.setHeader("content-type", getContentType(file.getFileType()));
        response.setContentType(getContentType(file.getFileType()));
        //2.设置文件头：最后一个参数是设置下载文件名(假如我们叫a.pdf)
        response.setHeader("Content-Disposition", "attachment;fileName="+file.getFileName());
        ServletOutputStream out;
        try {
            InputStream inputStream = fileUtil.getFile(file.getFilePath());
            if (Objects.isNull(inputStream)) {
                return;
            }
            //3.通过response获取ServletOutputStream对象(out)
            out = response.getOutputStream();
            int b = 0;
            byte[] buffer = new byte[512];
            while (b != -1){
                out.write(buffer,0,b);
                b = inputStream.read(buffer);
            }
            inputStream.close();
            out.close();
            out.flush();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public KbaseFileDto getFileInfo(String fileId, Integer version) {
        KbaseFile kbaseFile = super.getById(fileId);
        if (Objects.isNull(kbaseFile)) {
            return new KbaseFileDto();
        }
        KbaseFileDto kbaseFileDto = new KbaseFileDto();
        BeanUtils.copyProperties(kbaseFile, kbaseFileDto);
        if (Objects.nonNull(version)) {
            KbaseFileVersion fileVersion = versionService.getByFileIdAndVersion(fileId, version);
            if (Objects.nonNull(fileVersion)) {
                kbaseFileDto.setFilePath(fileVersion.getFileVersionPath());
                kbaseFileDto.setFileType(fileVersion.getFileType());
                kbaseFileDto.setFileVersion(fileVersion);
            }
        }
        kbaseFileDto.setTagList(tagService.getListByFileId(kbaseFileDto.getId()));
        return kbaseFileDto;
    }

    @Override
    public Page<KbaseFile> page(KbaseAuthUser user, KbaseFileVo vo) {
        List<String> userSubOrgList = orgService.getSubOrgIdList(user);
        if (CollectionUtils.isEmpty(userSubOrgList)) {
            throw new MyException(403, "无权查看");
        }
        List<String> fileSharedList = shareService.getSharedFileList(user);
        if (CollectionUtils.isEmpty(fileSharedList)) {
            fileSharedList.add("-1");
        }
        QueryWrapper<KbaseFile> qw = Wrappers.query();
        if (StringUtils.isNotBlank(vo.getTagName())) {
            List<String> fileIdList = fileTagService.getFileIdListByTagKeyword(vo.getTagName());
            if (CollectionUtils.isEmpty(fileIdList)) {
                return new Page(vo.getCurrent(), vo.getSize());
            }
            qw.in("ID", fileIdList);
        }
        qw.like(StringUtils.isNotBlank(vo.getFileName()), "FILE_NAME", vo.getFileName());
        qw.nested(q -> {
            q.nested(j-> {
                j.eq("CREATED_BY", user.getId());
            });
            q.or();
            q.nested(j-> {
                j.eq("VISIBLE_TYPE", VisibleTypeEnum.ORG.getKey())
                        .in("ORG_ID", userSubOrgList);
            });
            q.or();
            q.nested(j-> {
                j.eq("VISIBLE_TYPE", VisibleTypeEnum.ASSIGN.getKey())
                        .in("ID", fileSharedList);
            });
            q.or();
            q.nested(j-> {
                j.eq("VISIBLE_TYPE", VisibleTypeEnum.OPEN.getKey());
            });
        });
        qw.orderByDesc("UPDATED_TIME", "CREATED_TIME");
//        qw.or(i->{
//            i.nested(j-> {
//                j.eq("VISIBLE_TYPE", VisibleTypeEnum.SELF.getKey())
//                        .eq("CREATED_BY", user.getId()); });
//            i.nested(j -> {
//                j.eq("VISIBLE_TYPE", VisibleTypeEnum.ORG.getKey())
//                        .in("ORG_ID", userSubOrgList); });
//
//            i.nested(j -> {
//                j.eq("VISIBLE_TYPE", VisibleTypeEnum.ASSIGN.getKey())
//                        .in("ID", fileSharedList); });
//
//            i.nested(j-> {
//                j.eq("VISIBLE_TYPE", VisibleTypeEnum.OPEN.getKey()); });
//
//        });

        Page<KbaseFile> result = super.page(vo.getPage(), qw);
        if (CollectionUtils.isNotEmpty(result.getRecords())) {
            result.getRecords().parallelStream().forEach(i->{
                i.setTagList(tagService.getListByFileId(i.getId()));
            });
        }
        return result;
    }

    @Override
    public boolean isCreator(KbaseAuthUser user, String fileId) {
        KbaseFile file = super.getById(fileId);
        return Objects.nonNull(file) && StringUtils.equals(file.getCreatedBy(), user.getId());
    }

    @Override
    public Page<KbaseFile> currentUserPage(KbaseAuthUser user, Long current, Long size) {
        QueryWrapper<KbaseFile> qw = Wrappers.query();
        qw.eq("CREATED_BY", user.getId());
        Page<KbaseFile> result = super.page(new Page<KbaseFile>(current, size), qw);
        if (CollectionUtils.isNotEmpty(result.getRecords())) {
            result.getRecords().parallelStream().forEach(i->{
                i.setTagList(tagService.getListByFileId(i.getId()));
            });
        }
        return result;
    }

    @Override
    public List<KbaseFile> list(KbaseAuthUser user, String keyword) {
        List<String> userSubOrgList = orgService.getSubOrgIdList(user);
        if (CollectionUtils.isEmpty(userSubOrgList)) {
            throw new MyException(403, "无权查看");
        }
        List<String> fileSharedList = shareService.getSharedFileList(user);
        if (CollectionUtils.isEmpty(fileSharedList)) {
            fileSharedList.add("-1");
        }
        QueryWrapper<KbaseFile> qw = Wrappers.query();
        List<String> esFileIdList = Lists.newArrayListWithExpectedSize(0);
        if (StringUtils.isBlank(keyword)) {
            esFileIdList = esFileService.list().parallelStream().map(EsFile::getId).collect(Collectors.toList());
        } else {
            esFileIdList = esFileService.listByKeyword(keyword);
        }
        if (CollectionUtils.isEmpty(esFileIdList)) {
            return Lists.newArrayListWithExpectedSize(0);
        }
        qw.in("ID", esFileIdList);
        qw.nested(q -> {
            q.nested(j-> {
                j.eq("CREATED_BY", user.getId());
            });
            q.or();
            q.nested(j-> {
                j.eq("VISIBLE_TYPE", VisibleTypeEnum.ORG.getKey())
                        .in("ORG_ID", userSubOrgList);
            });
            q.or();
            q.nested(j-> {
                j.eq("VISIBLE_TYPE", VisibleTypeEnum.ASSIGN.getKey())
                        .in("ID", fileSharedList);
            });
            q.or();
            q.nested(j-> {
                j.eq("VISIBLE_TYPE", VisibleTypeEnum.OPEN.getKey());
            });
        });
        qw.orderByDesc("UPDATED_TIME", "CREATED_TIME");
        return super.list(qw);
    }

    private void updateFileVersion(String fileId, FileData fileData) {
        UpdateWrapper<KbaseFile> uw = Wrappers.update();
        uw.set("FILE_PATH", fileData.getFilePath())
                .set("FILE_TYPE", fileData.getFileType());
        uw.eq("id", fileId);
        super.update(uw);
    }



    public static String getContentType(String FilenameExtension) {
        if (FilenameExtension.equalsIgnoreCase("bmp")) {
            return "image/bmp";
        }
        if (FilenameExtension.equalsIgnoreCase("gif")) {
            return "image/gif";
        }
        if (FilenameExtension.equalsIgnoreCase("jpeg") ||
                FilenameExtension.equalsIgnoreCase("jpg") ||
                FilenameExtension.equalsIgnoreCase("png")) {
            return "image/jpeg";
        }
        if (FilenameExtension.equalsIgnoreCase("html")) {
            return "text/html";
        }
        if (FilenameExtension.equalsIgnoreCase("txt")) {
            return "text/plain";
        }
        if (FilenameExtension.equalsIgnoreCase("vsd")) {
            return "application/vnd.visio";
        }
        if (FilenameExtension.equalsIgnoreCase("pptx") ||
                FilenameExtension.equalsIgnoreCase("ppt")) {
            return "application/vnd.ms-powerpoint";
        }
        if (FilenameExtension.equalsIgnoreCase("docx") ||
                FilenameExtension.equalsIgnoreCase("doc")) {
            return "application/msword";
        }
        if (FilenameExtension.equalsIgnoreCase("xml")) {
            return "text/xml";
        }
        if (FilenameExtension.equalsIgnoreCase("pdf")) {
            return "application/pdf";
        }
        return "application/octet-stream";
    }

    private String readTextFromFile(String filePath) {
        File file = new File(filePath);
        BufferedReader reader = null;
        StringBuffer sbf = new StringBuffer();
        try {
            reader = new BufferedReader(new FileReader(file));
            String tempStr;
            while ((tempStr = reader.readLine()) != null) {
                sbf.append(tempStr);
            }
            reader.close();
            return sbf.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
        return sbf.toString();
    }




}
